package it.antonio.ilpizze.db.index.btree.file;

import it.antonio.ilpizze.db.index.btree.AbstractBTree;
import it.antonio.ilpizze.db.index.btree.Entry;
import it.antonio.ilpizze.db.index.btree.IntermediateNode;
import it.antonio.ilpizze.db.index.btree.LeafNode;
import it.antonio.ilpizze.db.index.utils.FPerst;
import it.antonio.ilpizze.db.index.utils.FilePointer;
import it.antonio.ilpizze.db.io.IOFile;

public class FileBTree<K extends Comparable<K>, V> extends AbstractBTree<K, V> implements FileBTreeConstants{

	private IOFile file;
	private FPerst<K> keyPersister;
	private FPerst<V> valuePersister;
	
	private int degree;
	
	private FileBTree(IOFile file, FPerst<K> keyPersister, FPerst<V> valuePersister) {
		this.file = file;
		this.keyPersister = keyPersister;
		this.valuePersister = valuePersister;
		
		file.position(0);
		degree = file.readInt();
		long rootPosition = file.readLong();
		if(rootPosition != -1){
			this.root = new FileIntermediateNode<K, V>(file, rootPosition, degree, keyPersister, valuePersister);	
		}
	}

	
	@Override
	protected int degree() {
		return degree;
	}

	@Override
	protected IntermediateNode<K, V> createRootNode(K key, int maxKeys) {
		IntermediateNode<K, V> newRoot = createIntermediateNode(key, maxKeys);
		FilePointer filePointer = (FilePointer) newRoot;
		file.position(SIZE_INT);
		file.writeLong(filePointer.position());
		return newRoot;
	}

	@Override
	protected IntermediateNode<K, V> createIntermediateNode(K key, int maxKeys) {
		file.position(SIZE_INT + SIZE_LONG);
		long position = file.readLong();
		
		file.position(position);
		file.writeInt(INTER_NODE);
		file.writeBoolean(false);
		keyPersister.persist(file, key);
		file.writeInt(0);
		for(int i = 0; i < degree; i++) file.writeLong(-1);
		
		FileIntermediateNode<K, V> node = new FileIntermediateNode<K, V>(file, position, degree, keyPersister, valuePersister);
		file.position(SIZE_INT + SIZE_LONG);
		file.writeLong(position + node.space());
		
		return node;
	}

	@Override
	protected LeafNode<K, V> createLeafNode(K key, int maxKeys) {
		file.position(SIZE_INT + SIZE_LONG);
		long position = file.readLong();
		
		
		file.position(position);
		file.writeInt(LEAF_NODE);
		file.writeBoolean(true);
		keyPersister.persist(file, key);
		file.writeInt(0);
		for(int i = 0; i < degree; i++) file.writeLong(-1);
		file.writeLong(-1);
		file.writeLong(-1);
		
		FileLeafNode<K,V> leaf = new FileLeafNode<K, V>(file, position, degree, keyPersister, valuePersister);
		file.position(SIZE_INT + SIZE_LONG);
		file.writeLong(position + leaf.space());
		
		return leaf;
	}

	@Override
	protected Entry<K, V> createEntry(K key, V value) {
		file.position(SIZE_INT + SIZE_LONG);
		long position = file.readLong();
		
		file.position(position);
		file.writeInt(ENTRY);
		keyPersister.persist(file, key);
		valuePersister.persist(file, value);
		
		FileEntry<K, V> entry = new FileEntry<K, V>(file, position, keyPersister, valuePersister);
		file.position(SIZE_INT + SIZE_LONG);
		file.writeLong(position + entry.space());
		
		return entry;
	}

	public static <K extends Comparable<K>, V> FileBTree<K,V> getOrCreate(IOFile file, int degree, FPerst<K> keyPersister, FPerst<V> valuePersister){
		if(file.size() == 0){
			file.writeInt(degree);
			file.writeLong(-1);
			file.writeLong(SIZE_INT + SIZE_LONG + SIZE_LONG);
		}
		return new FileBTree<K, V>(file, keyPersister, valuePersister);
		
	}
	
}
